
// Copyright (c) WanSheng Intelligent Corp. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.


#define LOG_TAG "api"


#include <unistd.h>
#include <ctype.h>
#include <sys/select.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <errno.h>
#include "coap_sdk.h"
#include "logs.h"
#include "misc.h"
#include "wa_edge_client_api.h"


struct WA_EDGE_CONTEXT_TAG 
{
    RESTFUL_CONTEXT agent_context;
    uip_ipaddr_t agent_addr[1] ;
    tick_time_t last_rd_send_ms;
    int rd_ttl;
    char * rd_message;
    char * di;
    WA_RD_Handler rd_handler;
    bool rd_registered;
};

WA_EDGE_CONTEXT wa_wagent_init(RESTFUL_CONTEXT rest_context, const char * wagent_IP)
{
    if(wagent_IP == NULL) wagent_IP = "127.0.0.1";
    WA_EDGE_CONTEXT context = (WA_EDGE_CONTEXT) malloc_z(sizeof(struct WA_EDGE_CONTEXT_TAG));
    context->agent_context = rest_context;
    set_addr_ip(context->agent_addr, (char*) wagent_IP, 5683);
    return context;
}


RESTFUL_CONTEXT wa_wagent_get_restful_context(WA_EDGE_CONTEXT iot_edge)
{
    return iot_edge->agent_context;
}

bool wa_wagent_is_rd_registered(WA_EDGE_CONTEXT agent)
{
    return agent->rd_registered;
}


restful_response_t * wa_wagent_request(WA_EDGE_CONTEXT context, unsigned int code,
        char *url, char * query, char *payload, wa_format_type_t format)
{
    restful_request_t request[1];
    wa_setup_request(request, code, format, payload?strlen(payload):0, payload, url, query);
    return wa_coap_request_wait(context->agent_context, request,context->agent_addr);
}


bool wa_wagent_simple_request(WA_EDGE_CONTEXT context, unsigned int code, char *url, char * query, char *payload, wa_format_type_t format)
{
    restful_response_t * response = wa_wagent_request(context, code, url, query, payload, format);
    if(response)
    {
        wa_free_response(response);
        return true;
    }
    return false;
}


char *wa_agent_query_rd_A(WA_EDGE_CONTEXT context)
{
    char * rd = NULL;
    restful_response_t * response = wa_wagent_request(context, COAP_GET, (char*)"/rd", NULL, NULL, IA_TEXT_PLAIN );
    if(response)
    {
        if(response->payload)
        {
            rd = response->payload;
            response->payload = NULL;
        }
        wa_free_response(response);
    }
    return rd;
}

typedef struct 
{
    WA_EDGE_CONTEXT agent;
    WA_RD_Handler user_callback;
} agent_request_user_data;

static void rd_register_handler (RESTFUL_CONTEXT context, restful_response_t *response, void * user_data)
{
    agent_request_user_data * agent_data = (agent_request_user_data*) user_data;
    WA_EDGE_CONTEXT agent = (WA_EDGE_CONTEXT)agent_data->agent;
    if(response && (response->code == CHANGED_2_04 || response->code == CREATED_2_01))
        agent->rd_registered = true;
    else
        agent->rd_registered = false;

    if(agent_data->user_callback)
        agent_data->user_callback(agent, agent->rd_registered);

    free(user_data);
}



void wa_wagent_post_rd(WA_EDGE_CONTEXT context, char * rd_message, WA_RD_Handler response_handler)
{
    restful_request_t request[1];
    wa_setup_request(request, COAP_POST, IA_APPLICATION_JSON, strlen(rd_message), rd_message,  (char*)"rd", NULL);
    agent_request_user_data * user_data = (agent_request_user_data*) malloc_z(sizeof(agent_request_user_data));
    user_data->agent = context;
    user_data->user_callback = response_handler;
    wa_coap_request_to_addr(context->agent_context, request, rd_register_handler, user_data,  context->agent_addr);
    context->last_rd_send_ms = bh_get_tick_ms();
}



// rd update only
void wa_wagent_update_rd(WA_EDGE_CONTEXT context, char * di, WA_RD_Handler response_handler)
{
    char query[200];
    snprintf(query, sizeof(query),"ep=%s", di);
    restful_request_t request[1];
    wa_setup_request(request, COAP_PUT, IA_APPLICATION_JSON, 0, NULL,  (char*)"rd", query);
    agent_request_user_data * user_data = (agent_request_user_data*) malloc_z(sizeof(agent_request_user_data));
    user_data->agent = context;
    user_data->user_callback = response_handler;    
    wa_coap_request_to_addr(context->agent_context, request, rd_register_handler, user_data,  context->agent_addr);
    context->last_rd_send_ms = bh_get_tick_ms();
}

void wa_wagent_set_rd(WA_EDGE_CONTEXT context, const char * device_id, const char * rd_message, int ttl_ms, WA_RD_Handler rd_status_handler)
{
    if(context->rd_message) free(context->rd_message);
    if(rd_message)
        context->rd_message = strdup(rd_message);
    else
        context->rd_message = NULL;
    
    if(context->di) free(context->di);
    context->di = device_id?strdup(device_id):NULL;

    context->rd_ttl = ttl_ms;
    context->rd_handler = rd_status_handler;
}


uint32_t wa_agent_check_rd_registeration(WA_EDGE_CONTEXT context)
{
    if(context->rd_ttl <= 0 || context->rd_message == NULL || context->di == NULL)
        return -1;

    int next = context->rd_registered?context->rd_ttl / 2.5:context->rd_ttl;
    tick_time_t now = bh_get_tick_ms();
    if(now > (context->last_rd_send_ms + next))
    {
        if(context->rd_registered)
            wa_wagent_update_rd(context, context->di, context->rd_handler);
        else
            wa_wagent_post_rd(context, context->rd_message, context->rd_handler);
        
        // ensure two 
        return next;
    }
    else
    {
        return (uint32_t)(context->last_rd_send_ms + next - now);
    }
}
